#include <iostream>
#include <map>

#include "foreach.hpp"
#include "model.hpp"
#include "random.hpp"
#include "texture.hpp"
#include "string_utils.hpp"
#include "terrain.hpp"
#include "wml_node.hpp"
#include "wml_utils.hpp"

namespace hex {

namespace {
std::map<std::string, terrain_ptr> cache;
}

void terrain::init(const wml::const_node_ptr& node)
{
	wml::node::const_child_iterator i1 = node->begin_child("terrain");
	wml::node::const_child_iterator i2 = node->end_child("terrain");
	for(; i1 != i2; ++i1) {
		terrain_ptr t(new terrain(i1->second));
		cache[t->id()] = t;
		std::cerr << "added terrain: '" << t->id() << "'\n";
	}
}

const_terrain_ptr terrain::get(const std::string& key)
{
	return cache[key];
}

terrain::terrain(const wml::const_node_ptr& node)
  : id_(node->attr("id")), move_cost_(wml::get_int(node, "move_cost", 10)),
    water_(wml::get_bool(node, "water", false)),
    detail_level_(wml::get_int(node, "detail_level", -1)),
    num_models_(wml::get_int(node, "num_models", 0)),
	heights_(game_logic::formula::create_optional_formula(node->attr("heights"))),
    bonus_resource_formula_(game_logic::formula::create_optional_formula(node->attr("bonus_resource_formula")))
{
	std::vector<std::string> images = util::split(node->attr("image"));
	foreach(const std::string& image, images) {
		textures_.push_back(graphics::texture::get(image));
		if(textures_.back().valid() == false) {
			std::cerr << "INVALID IMAGE: " << image << "\n";
		}
	}

	if(textures_.empty()) {
		std::cerr << "terrain has no images\n";
		throw parse_error();
	}

	std::vector<std::string> models = util::split(node->attr("model"));
	foreach(const std::string& m, models) {
		models_.push_back(graphics::model::get(m));
		if(!models_.back()) {
			std::cerr << "INVALID MODEL: " << m << "\n";
		}
	}

	const std::string underlying_terrain = node->attr("underlying");
	if(underlying_terrain.empty() == false) {
		underlying_ = get(underlying_terrain);
	}

	wml::node::const_child_iterator i1 = node->begin_child("resource");
	wml::node::const_child_iterator i2 = node->end_child("resource");
	for(; i1 != i2; ++i1) {
		const std::string& id = i1->second->attr("id");
		game_logic::const_formula_ptr formula = game_logic::formula::create_optional_formula(i1->second->attr("quality"));
		if(formula) {
			resources_[id] = formula;
		}
	}
}

namespace {
int prandom(const location& loc)
{
	const unsigned int a = (loc.x() + 92872873) ^ 918273;
	const unsigned int b = (loc.y() + 1672517) ^ 128123;
	return a*b + a + b;
}
}

const graphics::texture& terrain::get_texture(const location& loc) const
{
	return textures_[prandom(loc)%textures_.size()];
}

const std::vector<graphics::texture>& terrain::get_textures() const
{
	return textures_;
}

graphics::const_model_ptr terrain::get_model(rng& gen) const
{
	if(models_.empty()) {
		return graphics::const_model_ptr();
	}
	return models_[gen.generate()%models_.size()];
}

int terrain::move_cost() const
{
	return (underlying_ ? underlying_->move_cost() : 0) + move_cost_;
}

std::pair<std::string,int> terrain::generate_bonus_resource() const
{
	if(!bonus_resource_formula_) {
		return std::pair<std::string,int>("",0);
	}

	variant result = bonus_resource_formula_->execute();
	if(!result.is_list() || result.num_elements() != 2) {
		return std::pair<std::string,int>("",0);
	}

	return std::pair<std::string,int>(result[0].as_string(), result[1].as_int());
}

std::vector<int> terrain::generate_heights() const
{
	std::vector<int> res;
	if(heights_) {
		variant v = heights_->execute();
		if(v.is_list()) {
			for(int n = 0; n != v.num_elements(); ++n) {
				res.push_back(v[n].as_int());
			}
		}
	}

	return res;
}

}
